import httpx
import json
from typing import Any, Dict
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
import os
from datetime import datetime

# 初始化FastMCP服务器
mcp = FastMCP("weatherServer")

# 加载环境变量
load_dotenv(override=True)

# API配置
OPENWEATHER_API_KEY = os.getenv('OPENWEATHER_API_KEY')
BASE_URL = "https://api.openweathermap.org/data/2.5"
DEFAULT_UNITS = "metric"
DEFAULT_LANG = "zh_cn"
USER_AGENT = "weather-app/1.0"
CACHE_TTL = 600  # 缓存10分钟

# 简单缓存类
class SimpleCache:
    def __init__(self, ttl: int):
        self._cache: Dict[str, tuple] = {}  # (value, timestamp)
        self.ttl = ttl

    def get(self, key: str) -> Any:
        if key in self._cache:
            value, timestamp = self._cache[key]
            if (datetime.now().timestamp() - timestamp) < self.ttl:
                return value
            else:
                del self._cache[key]
        return None

    def set(self, key: str, value: Any):
        self._cache[key] = (value, datetime.now().timestamp())


# 实例化缓存
cache = SimpleCache(ttl=CACHE_TTL)

# HTTP客户端
client = httpx.AsyncClient(
    headers={"User-Agent": USER_AGENT},
    timeout=30.0
)


async def make_request(city: str, forecast: bool = False) -> dict[str, Any] | None:
    """Make a request to the API with proper error handling.
    从 OpenWeather API 获取天气信息。
    :param city: 城市名称（需使用英文，如 Beijing）
    :param forecast: 是否为预报请求
    :return: 天气数据字典；若出错返回包含 error 信息的字典
    """
    # 解析 location：判断是坐标还是城市名
    if "," in city:
        try:
            lat, lon = city.split(",", 1)
            lat = lat.strip()
            lon = lon.strip()
            # 简单判断是否为数字
            if lat.replace("-", "").replace(".", "").isdigit() and lon.replace("-", "").replace(".", "").isdigit():
                params = {"lat": lat, "lon": lon}
            else:
                params = {"q": city}
        except:
            params = {"q": city}
    else:
        params = {"q": city}

    # 添加公共参数
    params.update({
        "appid": OPENWEATHER_API_KEY,
        "units": DEFAULT_UNITS,
        "lang": DEFAULT_LANG
    })

    # 选择API端点
    if forecast:
        url = f"{BASE_URL}/forecast"
        params["cnt"] = 40  # 5天预报，每3小时一次，共40条
    else:
        url = f"{BASE_URL}/weather"

    try:
        response = await client.get(url, params=params)
        response.raise_for_status()
        return response.json()
    except httpx.HTTPStatusError as e:
        return {"error": f"HTTP 错误: {e.response.status_code}"}
    except Exception as e:
        return {"error": f"请求失败: {str(e)}"}


def format_current_weather(data: dict[str, Any] | str) -> str:
    """
    将当前天气数据格式化为易读文本，强制包含指定关键字。
    """
    # 如果传入的是字符串，则先转换为字典
    if isinstance(data, str):
        try:
            data = json.loads(data)
        except Exception as e:
            return f"无法解析天气数据: {e}"

    # 如果数据中包含错误信息，直接返回错误提示
    if "error" in data:
        return f"{data['error']}"

    # 提取数据时做容错处理
    city = data.get("name", "未知")
    country = data.get("sys", {}).get("country", "未知")
    temp = data.get("main", {}).get("temp", "N/A")
    feels_like = data.get("main", {}).get("feels_like", "N/A")
    humidity = data.get("main", {}).get("humidity", "N/A")
    wind_speed = data.get("wind", {}).get("speed", "N/A")
    weather_list = data.get("weather", [{}])
    description = weather_list[0].get("description", "未知")

    return (
        f"{city}, {country}\n"
        f"天气状况: {description}\n"
        f"实时温度: {temp}°C \n"
        f"体感温度: {feels_like}°C)\n"
        f"湿度: {humidity}%\n"
        f"风速: {wind_speed} m/s\n"
    )


def format_forecast_weather(data: dict[str, Any] | str) -> str:
    """
    将预报天气数据格式化为易读文本，强制包含指定关键字。注意：预报数据中”实时温度“替换为“”
    """
    # 如果传入的是字符串，则先转换为字典
    if isinstance(data, str):
        try:
            data = json.loads(data)
        except Exception as e:
            return f"无法解析天气数据: {e}"

    # 如果数据中包含错误信息，直接返回错误提示
    if "error" in data:
        return f"{data['error']}"

    # 提取位置信息
    location_name = data.get("city", {}).get("name", "未知")
    country = data.get("city", {}).get("country", "未知")

    # 按日期分组预报
    daily_forecasts = {}
    for item in data.get("list", []):
        date = datetime.fromtimestamp(item["dt"]).date().isoformat()
        if date not in daily_forecasts:
            daily_forecasts[date] = []

        daily_forecasts[date].append({
            "time": datetime.fromtimestamp(item["dt"]).strftime("%H:%M"),
            "temp": item["main"]["temp"],
            "feels_like": item["main"]["feels_like"],
            "description": item["weather"][0]["description"],
            "humidity": item["main"]["humidity"],
            "wind_speed": item["wind"]["speed"]
        })

    # 构建输出字符串
    result = [f"{location_name}, {country} 未来5天天气预报\n"]

    for date, forecasts in daily_forecasts.items():
        date_str = datetime.fromisoformat(date).strftime("%Y年%m月%d日")

        # 取当天第一个预报点作为代表
        sample = forecasts[0]
        max_temp = max(f["temp"] for f in forecasts)
        min_temp = min(f["temp"] for f in forecasts)
        main_desc = max(set(f["description"] for f in forecasts), key=[f["description"] for f in forecasts].count)

        result.append(f"\n{date_str}:")
        result.append(f"天气状况: {main_desc}")
        result.append(f"实时温度: {sample['temp']}°C (最高{max_temp}°C / 最低{min_temp}°C)")
        result.append(f"体感温度: {sample['feels_like']}°C")
        result.append(f"湿度: {sample['humidity']}%")
        result.append(f"风速: {sample['wind_speed']} m/s")

    return "\n".join(result)


@mcp.tool()
async def query_weather_today(city: str) -> str:
    """
    输入指定城市的英文名字,返回今日天气查询结果
    :param city: 城市名称（如 "Beijing"）
    :return: 格式化后的天气信息
    """
    # 缓存键
    cache_key = f"current:{city}"
    cached = cache.get(cache_key)
    if cached:
        return f"来自缓存:\n{format_current_weather(cached)}"

    data = await make_request(city, forecast=False)
    if "error" not in data:
        cache.set(cache_key, data)
    return format_current_weather(data)


@mcp.tool()
async def query_weather_5days(city: str) -> str:
    """
    输入指定城市的英文名字,返回未来五天的天气预测结果
    :param city: 城市名称（如 "Beijing"）
    :return: 格式化后的天气预报信息
    """
    # 缓存键
    cache_key = f"forecast:{city}"
    cached = cache.get(cache_key)
    if cached:
        return f"来自缓存:\n{format_forecast_weather(cached)}"

    data = await make_request(city, forecast=True)
    if "error" not in data:
        cache.set(cache_key, data)
    return format_forecast_weather(data)


if __name__ == "__main__":
    mcp.run(transport='stdio')